/////////////////////////////////////////////////////////////////////////////
// Name:        ax_resource_library.h
// Purpose:     Accessibility classes: AxResourceLibrary
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef __AX_RESOURCELIBRART_H_
#define __AX_RESOURCELIBRART_H_

#include "wx/dynarray.h"

#include "ax_resource/ax_resource.h"
#include "ax_resource/ax_supportedfiles.h"

class wxOutputStream;
class wxInputStream;
class wxDataOutputStream;
class wxDataInputStream;
class AxResourceLibrary;
class wxEditorCommandManager;
class wxDataStreamHelper;

#define axLIBRARY_FORMAT_VERSION 1.0

/**
    @class AxResourceInfo
    
    Holds an AxResource object.

    @category{ax_resource}
 */

class AxResourceInfo: public wxObject
{
public:
    AxResourceInfo();
    AxResourceInfo(const AxResourceInfo& info) { Init(); Copy(info); }
    ~AxResourceInfo();

    /// Init
    void Init();

    void Copy(const AxResourceInfo& info);
    void operator=(const AxResourceInfo& info) { Init(); Copy(info); }
    bool operator==(const AxResourceInfo& info) const;

    bool Read(wxDataStreamHelper& helper);
    bool Write(wxDataStreamHelper& helper);

    /// Get/set resource
    AxResource* GetResource() const { return m_resource; }
    void SetResource(AxResource* resource) { m_resource = resource; }

    /// Get/set modified flag
    bool GetModified() const;
    void SetModified(bool mod);

    /// Get/set owns resource 
    bool GetOwnsResource() const { return m_ownsResource; }
    void SetOwnsResource(bool owns) { m_ownsResource = owns; }

    /// Clear all
    void Clear();

    //int             m_bookType;
    //int             m_bookStatus; // owned, or on wishlist

    bool            m_deleted;
    long            m_recordIdentifier;
    AxResource*     m_resource;
    bool            m_ownsResource;

    //wxImageBlock    m_thumbnailImageBlock;
    //cdCoverDesign   m_coverDesign;
};

WX_DECLARE_OBJARRAY(AxResourceInfo, AxResourceInfoArray);

/*
   AxFolder: represents a folder containing files; for example, a music album.
 */

class AxFolder
{
public:
    AxFolder() {}
    AxFolder(const AxFolder& folder) {  Copy(folder); }

    void Copy(const AxFolder& folder) { m_filenames = folder.m_filenames; m_folderName = folder.m_folderName; }
    void operator= (const AxFolder& folder) { Copy(folder); }

    wxArrayString   m_filenames;
    wxString        m_folderName;
};

WX_DECLARE_OBJARRAY(AxFolder, AxFolders);

/**
    @class AxResourceSet
    
    Contains an array of resource pointers, typically from the result of a search.

    @category{ax_resource}
 */

class AxResourceSet: public wxObject
{
public:
    AxResourceSet(AxResourceLibrary* library = NULL);
    AxResourceSet(const AxResourceSet& set) { Init(); Copy(set); }

    /// Init
    void Init();

    void Copy(const AxResourceSet& set);
    void operator=(const AxResourceSet& set) { Init(); Copy(set); }
    bool operator==(const AxResourceSet& set) const;

    /// Get resource
    AxResource* GetResource(int idx) const;

    /// Get library
    AxResourceLibrary* GetResourceLibrary() const { return m_library; }

    /// Set library
    void SetResourceLibrary(AxResourceLibrary* library) { m_library = library; }

    /// Clear all
    void Clear();

    /// Get resources
    AxResourceArray& GetResources() { return m_resources; }
    const AxResourceArray& GetReources() const { return m_resources; }

    /// Get count
    size_t GetCount() const { return m_resources.GetCount(); }

    AxResourceArray     m_resources;
    AxResourceLibrary*  m_library;
};

/**
    @class AxResourceLibrarySettings
    
    @category{ax_resource}
 */

class AxResourceLibrarySettings: public wxObject
{
public:

    AxResourceLibrarySettings() { Init(); }
    AxResourceLibrarySettings(const AxResourceLibrarySettings& settings) { Copy(settings); }
    ~AxResourceLibrarySettings();

    void Init();
    void operator=(const AxResourceLibrarySettings& settings) { Copy(settings); }
    void Copy(const AxResourceLibrarySettings& settings);

    /// Set/get main library path
    void SetMainLibraryPath(const wxString& path) { m_mainLibraryPath = path; }
    const wxString& GetMainLibraryPath() const { return m_mainLibraryPath; }

    /// Set/get additional library paths
    void SetAdditionalLibraryPaths(const wxArrayString& paths) { m_additionalLibraryPaths = paths; }
    const wxArrayString& GetAdditionalLibraryPaths() const { return m_additionalLibraryPaths; }
    wxArrayString& GetAdditionalLibraryPaths() { return m_additionalLibraryPaths; }

    /// Set/get full library data filename
    void SetLibraryDataFile(const wxString& path) { m_libraryDataFile = path; }
    const wxString& GetLibraryDataFile() const { return m_libraryDataFile; }

    /// Get changes filename, deducing it from the library data filename
    wxString GetLibraryChangesDataFile() const;

    /// Set supported files
    void SetSupportedFiles(const AxSupportedFiles& supported) { m_supportedFiles = supported; }

    /// Get supported files
    AxSupportedFiles& GetSupportedFiles() { return m_supportedFiles; }
    const AxSupportedFiles& GetSupportedFiles() const { return m_supportedFiles; }

    wxString                m_mainLibraryPath;
    wxArrayString           m_additionalLibraryPaths;
    wxString                m_uploadPath;
    wxString                m_libraryDataFile;
    AxSupportedFiles        m_supportedFiles;
};

/**
    @class AxResourceLibrary
    
    @category{ax_resource}
 */

// Search flags
#define axSEARCH_EXACT_MATCH    0x01
#define axSEARCH_CONTAINS       0x02
#define axSEARCH_IGNORE_CASE    0x04

// I/O flags
#define axWRITE_CHANGES_ONLY    0x01
#define axREAD_MERGE            0x02

//  Query flags
#define axISBN_QUERY_EXACT_MATCH 0x01

class AxResourceLibrary: public wxObject
{
public:

    AxResourceLibrary() { Init(); }
    AxResourceLibrary(const AxResourceLibrary& library) { Copy(library); }
    ~AxResourceLibrary();

    void Init();
    void operator=(const AxResourceLibrary& library) { Copy(library); }
    void Copy(const AxResourceLibrary& library);

    /// Initialize modules etc.
    virtual bool Initialize();

    /// Uninitialize modules etc.
    virtual bool Uninitialize();

    /// Gets the (one) instance of the manager
    static AxResourceLibrary* GetResourceLibrary() { return sm_library; }

    /// Create a working set from the currently loaded books
    virtual void CreateFullWorkingSet();
    virtual void CreateFullWorkingSet(AxResourceSet& workingSet);

    /// Create a working set from a specification. If field is empty,
    /// allow any field to match
    virtual void CreateWorkingSetFromSearch(const wxString& field, const wxString& value, int flags = axSEARCH_EXACT_MATCH);
    virtual void CreateWorkingSetFromSearch(AxResourceSet& workingSet, const wxString& field, const wxString& value, int flags = axSEARCH_EXACT_MATCH);

    /// Apply search to current working set
    virtual void ApplySearchToWorkingSet(const wxString& field, const wxString& value, int flags);
    virtual void ApplySearchToWorkingSet(AxResourceSet& workingSet, const wxString& field, const wxString& value, int flags);

    /// Match?
    virtual bool Match(AxResource& resource, const wxString& field, const wxString& value, int flags);

    /// Refresh the display using current settings
    /// TODO reflect current settings!
    virtual bool RefreshAll();

    /// Load books from a folder
    virtual bool LoadFromFolder(const wxString& folder, bool clearFirst = true, bool checkIfFileLoaded = false);
    virtual bool LoadFromFolder(AxResourceInfoArray& books, const wxString& folder, bool clearFirst = true, bool checkIfFileLoaded = false, bool allocId = true);

    /// Load books from a set of folders
    virtual bool LoadFromFolders(const wxArrayString& folders, bool clearFirst = true, bool checkIfFileLoaded = false);

    /// Refresh the library
    virtual bool RefreshLibrary(bool clearAll, bool interactive, wxWindow* parent = NULL);

    /// Load the library
    virtual bool LoadLibrary();

    /// Save the library
    virtual bool SaveLibrary(bool forceConsolidatedWrite = false);

    /// Load resource from file
    virtual AxResource* CreateResourceFromFile(const wxString& filename);

    /// Aggregate any files that belong together and add to books
    bool AggregateFiles(AxResourceInfoArray& books, wxArrayString& files, bool checkIfFileLoaded, bool allocId);

    /// Load image from cover book or default design
    bool LoadImageFromCoverDesign(AxResourceInfo& info, const wxSize& thumbnailImageSize);

    /// Add resource
    int AddResource(AxResource* resource);

    /// Add resource
    int AddResource(AxResourceInfo& info, bool isNew);

    /// Remove resources from library
    bool RemoveSelectedBooks(bool interactive = true, wxWindow* win = NULL);

    /// Remove resource from library
    bool RemoveResource(int sel, bool interactive = true, wxWindow* win = NULL);

    /// Purge files that no longer exist
    bool PurgeDeletedFiles();

    /// Recursively find all supported files
    bool RecursivelyFindFiles(const wxString& folder, AxFolders& folders);

    /// Load information from epub file
    bool LoadEpubBookInfo(const wxString& filename, AxResourceInfo& info, bool makeThumbnail, const wxSize& thumbnailImageSize, bool& foundCoverImage);

    /// Is this file supported?
    bool IsFileSupported(const wxString& filename) const;

    /// Is this file in the index?
    bool IsFileInIndex(const wxString& filename) const;
    bool IsFileInIndex(const AxResourceInfoArray& books, const wxString& filename) const;

    /// Find by file
    int FindByFile(const wxString& filename) const;
    int FindByFile(const AxResourceInfoArray& books, const wxString& filename) const;

    /// Find by unique record identifier
    AxResourceInfo* FindByRecordIdentifier(long id) const;

    /// Write HTML summary for this book
    wxString CreateHtmlSummary(AxResourceInfo& info, const wxColour& backgroundColour, const wxColour& textColour);

    /// Write HTML blank page
    wxString CreateHtmlBlankSummary(const wxColour& backgroundColour, const wxColour& textColour);

    /// Compare two items for sorting purposes
    int CompareItems(AxResource& item1, AxResource& item2, int col);
    int CompareItemsHelper(AxResource& item1, AxResource& item2, int col);

    /// Sort the working set
    virtual void Sort(int col);

    /// Update main panel according to current document
    void UpdateSelectedBookInfo(int idx, bool forceClear = false);

    virtual bool Read(wxInputStream& stream, wxDataInputStream& dataStream, int flags = 0);
    virtual bool Write(wxOutputStream& stream, wxDataOutputStream& dataStream, int flags = 0);

    virtual bool Read(const wxString& filename, int flags = 0);
    virtual bool Write(const wxString& filename, int flags = 0);

    /// Decide whether we can save modifications seperately, or whether we should
    /// write out the whole file and delete the modifications file.
    virtual bool CanWriteChangesSeparately(int percentageThreshold = 20);

    /// Clear all
    virtual void Clear();

    /// Get/set last (highest) identifier
    long GetLastIdentifier() const { return m_lastIdentifier; }
    void SetLastIdentifier(long id) { m_lastIdentifier = id; }

    /// Allocate new identifier
    long AllocateNewIdentifier();

    /// Get books
    const AxResourceInfoArray& GetBooks() const { return m_resources; }
    AxResourceInfoArray& GetBooks() { return m_resources; }

    /// Get/set modified flag
    bool GetModified() const { return m_modified; }
    void SetModified(bool mod) { m_modified = mod; }

    /// Get the current keyword arrays
    wxArrayString& GetCurrentAuthors() { return m_currentAuthors; }
    wxArrayString& GetCurrentPublishers() { return m_currentPublishers; }
    wxArrayString& GetCurrentSubjects() { return m_currentSubjects; }
    wxArrayString& GetCurrentFormats() { return m_currentFormats; }

    /// Add to current keyword arrays
    void AddToCurrentAuthors(const wxString& author);
    void AddToCurrentPublishers(const wxString& publisher);
    void AddToCurrentSubjects(const wxString& subject);
    void AddToCurrentFormats(const wxString& format);

    /// Add to current author etc. arrays
    void AddCurrentInfo(const AxResourceInfo& info);

    /// Get selected item (index into the array of books)
    int GetSelection() const { return m_selection; }

    /// Get selected item (index into the array of books)
    AxResourceInfo* GetSelectedBook() const;

    /// Set selection
    void SetSelection(int sel, bool select = true);

    /// Set single selection, clearing others
    void SetSingleSelection(int sel);

    /// Clear selections
    void ClearSelections();

    /// Get all selections
    bool GetSelections(wxArrayInt& selections);

    void DisableProcessing(bool disable) { m_disableProcessing = disable; }
    bool IsProcessingDisabled() const { return m_disableProcessing; }

    /// Perform a search
    bool DoSearch(const wxString& searchText);

    /// Get the default cover design
    // cdCoverDesign& GetDefaultCoverDesign() { return m_coverDesign; }

    /// Get ebook download manager
    // shEbookDownloadManager& GetDownloadManager() { return m_downloadManager; }

    /// Set/get cover summary/thumbnail sizes
    const wxSize& GetSummaryCoverSize() const { return m_summaryCoverSize; }
    const wxSize& GetThumbnailCoverSize() const { return m_thumbnailCoverSize; }
    void SetSummaryCoverSize(const wxSize& sz) { m_summaryCoverSize = sz; }
    void SetThumbnailCoverSize(const wxSize& sz) { m_thumbnailCoverSize = sz; }

    /// Instantiate the keyword provider
    // void InstantiateKeywords(AxResourceInfo& info, cdKeywordProvider& keywordProvider);

    /// Set/get column to sort on
    int GetSortByCol() const { return m_sortByCol; }
    void SetSortByCol(int col) { m_sortByCol = col; }

    /// Get the root of the sites branch
    // const wxTreeItemId& GetSitesRootItem() const { return m_sitesRoot; }

    /// Upload files
    bool UploadFiles(wxWindow* parent);

    /// Can upload files?
    bool CanUploadFiles() const;

    /// Set settings
    void SetSettings(const AxResourceLibrarySettings& settings) { m_settings = settings; }

    /// Get settings
    AxResourceLibrarySettings& GetSettings() { return m_settings; }
    const AxResourceLibrarySettings& GetSettings() const { return m_settings; }

    /// Set//get the optional command manager object that can be used to launch viewers.
    /// TODO: consider replacing with a virtual function that can be overridden
    /// by a derived library class. However, we may wish to give all resource
    /// handling functionality more than just 'launch this file'.
    void SetCommandManager(wxEditorCommandManager* manager) { m_commandManager = manager; }
    wxEditorCommandManager* GetCommandManager() const { return m_commandManager; }

private:
    AxResourceInfoArray         m_resources;

    AxResourceSet               m_workingSet;
    bool                        m_modified;

    wxArrayString               m_currentAuthors;
    wxArrayString               m_currentPublishers;
    wxArrayString               m_currentSubjects;
    wxArrayString               m_currentFormats;

    int                         m_selection;
    int                         m_sortByCol;
    bool                        m_disableProcessing;

    wxSize                      m_summaryCoverSize;
    wxSize                      m_thumbnailCoverSize;

    long                        m_lastIdentifier;

    AxResourceLibrarySettings   m_settings;

    wxEditorCommandManager*     m_commandManager;
    
    static AxResourceLibrary*   sm_library;

    // cdCoverDesign            m_coverDesign;
};

/// Find index of book by ISBN
int axFindByISBN(const AxResourceInfoArray& books, const wxString& isbn);

#endif
    // __AX_RESOURCELIBRART_H_
